#include"include.h"

torrent_t *torrentInit(list_t *l)
{
   //Initialize fields to default values
   torrent_t *t = malloc(sizeof(torrent_t));
   t->priv = 0;
   t->pieceLen = 0;
   t->pieces = NULL;
   t->piecesLen = 0;
   t->announce = NULL;
   t->singleFile = 1;
   t->totalSize = 0;
   t->totalSeeders = 0;
   t->totalLeechers = 0;
   t->status = STAT_STOPPED;
   t->downloaded = 0;
   t->uploaded = 0;
   t->left = 0;
   t->inactpeers= createList();
   t->actpeers = createList();
   t->lastPeerShuffle = 0;
   t->shuffleInterval = 5;
   t->lastAnnounce = 0;
   t->announceInterval = 60 * 30;
   t->minAnnounceInterval = 0;

   resetList(l);
   dict_t *d = ((be_dat *)getObject(l))->dat.d; //Torrents are one big dict
   t->announce = ((be_dat *)dictGet(d, "announce"))->dat.s->s;

   dict_t *info = ((be_dat *)dictGet(d, "info"))->dat.d;
   t->infohash = info->hash;

   t->pieceLen = ((be_dat *)dictGet(info, "piece length"))->dat.i;
   t->pieces = ((be_dat *)dictGet(info, "pieces"))->dat.s->s;
   //Live little mystr struct! This is your moment. This is what 
   //you were created for! To make this one line possible...
   t->piecesLen = ((be_dat *)dictGet(info, "pieces"))->dat.s->len;

   if(dictContainsKey(info, "private"))
      t->priv = ((be_dat *)dictGet(info, "private"))->dat.i;

   if(dictContainsKey(info, "files"))
      t->singleFile = 0;

   if(t->singleFile)
   {
      sfile *file = malloc(sizeof(sfile));
      file->name = ((be_dat *)dictGet(info, "name"))->dat.s->s;
      file->len = ((be_dat *)dictGet(info, "length"))->dat.i;
      t->file.s = file;
      t->totalSize += file->len;
   }
   else
   {
      mfile *file = malloc(sizeof(mfile));
      file->name = ((be_dat *)dictGet(info, "name"))->dat.s->s;
      list_t *files = ((be_dat *)dictGet(info, "files"))->dat.l;
      list_t *sfiles = createList();
      
      t->file.m = file;

      resetList(files);
      while(!endList(files))
      {
	 dict_t *afile = ((be_dat *)getObject(files))->dat.d;
	 sfile *asfile = malloc(sizeof(sfile));
	 
	 char *fileName = NULL;
	 int fileNameLen = 0;

	 list_t *pathParts = ((be_dat *)dictGet(afile, "path"))->dat.l;
	 char *pathPart;
	 for(resetList(pathParts);!endList(pathParts);)
	 {
	    pathPart = ((be_dat *)getObject(pathParts))->dat.s->s;
	    fileName = realloc(fileName, fileNameLen + strlen(pathPart) + 1);
	    int i;
	    for(i=0;i<strlen(pathPart);i++)
	    {
	       fileName[fileNameLen+i] = pathPart[i];
	    }
	    if(endList(pathParts))
	       fileName[fileNameLen+i] = '\0';
	    else
	    {
	       fileName[fileNameLen+i] = '/';
	       fileNameLen += i + 1;
	    }
	 }

	 asfile->name = fileName;
	 addObject(sfiles, asfile);
	 asfile->len = ((be_dat *)dictGet(afile, "length"))->dat.i;
	 t->totalSize += asfile->len;
      }
      file->files = sfiles;
   }
   
   unsigned long calcedPiecesLen = ceil((double)t->totalSize / (double)t->pieceLen) * 20;
   t->left = t->totalSize;

   if(t->piecesLen % 20 != 0)
   {
      loggerLog(LOG_ERR, "Torrent pieces hash was not a multiple of 20.\n");
      return NULL;
   }
   if(calcedPiecesLen != t->piecesLen)
   {
      loggerLog(LOG_ERR, "Pieces length did not agree with size.\n");
      return NULL;
   }
   if(t->pieceLen == 0)
   {
      loggerLog(LOG_ERR,"Torrent did not specify piece length\n");
      return NULL;
   }
   if(t->pieces == NULL)
   {
      loggerLog(LOG_ERR,"Torrent did not specify pieces\n");
      return NULL;
   }
   
   t->piecebitfield = bitfieldInit(ceil((double)t->totalSize / (double)t->pieceLen));
   t->activeReqs = newDict((int (*)(const void *, const void *))integerCompare);
   t->activeReqsRcved = newDict((int (*)(const void *, const void *))integerCompare);

   return t;
}

int torrentTrackerAnnounce(torrent_t *t, char *peer_id, int port)
{
   if(mystrStartsWith(t->announce, "http://"))
   {
      dict_t *parms = newDict( (int (*)(const void *, const void *))strcmp);
      dictPut(parms, "info_hash", urlencode(t->infohash, 20));
      dictPut(parms, "peer_id", peer_id);
      char *sport = malloc(100);
      char *uploaded = malloc(100);
      char *downloaded= malloc(100);
      char *left= malloc(100);
      sprintf(sport, "%d", port);
      dictPut(parms, "port", sport);
      sprintf(uploaded, "%lld", t->uploaded);
      dictPut(parms, "uploaded", uploaded);
      sprintf(downloaded, "%lld", t->downloaded);
      dictPut(parms, "downloaded", downloaded);
      sprintf(left, "%lld", t->left);
      dictPut(parms, "left", left);
      dictPut(parms, "compact", "1");

      //ALter the state to what it should be now
      if(t->status == STAT_STARTING)
      {
         loggerLog(LOG_INFO, "Sending started event for torrent %s.\n", torrentGetName(t));
         dictPut(parms, "event", "started");
         t->status = (t->left == 0) ? STAT_SEEDING : STAT_LEECHING;
      }
      else if(t->status == STAT_STOPPING)
      {
         loggerLog(LOG_INFO, "Sending stopped event for torrent %s.\n", torrentGetName(t));
         dictPut(parms, "event", "stopped");
         t->status = STAT_STOPPED;
      }
      else if(t->status == STAT_COMPLETING)
      {
         dictPut(parms, "event", "completed");
         t->status = STAT_SEEDING;
      }


      mystr *bod = httpGetUrlBody(t->announce, parms);
      list_t *resp = bdecode(bod->s, bod->len);
      //dictClearKeysVals(parms); free(parms);
      dictClear(parms);
      free(parms);
      free(bod->s);
      free(bod);

      if(resp == NULL)
      {
         loggerLog(LOG_ERR, "Null response\n");
	 return 0;
      }
      //We got responded too. Yay.
      t->lastAnnounce = gettime();
      resetList(resp);
      be_dat *d = getObject(resp);
      dict_t *dic = d->dat.d;
      if(dictContainsKey(dic, "failure reason"))
      {
	 be_dat *failReason = dictGet(dic, "failure reason");
	 if(failReason->type != BE_STR)
	 {
	    loggerLog(LOG_ERR, "Malformed tracker data: 'failure reason's value was not a string.\n");
	    return 0;
	 }
	 loggerLog(LOG_WARN,"Tracker announce failed because of %s\n", failReason->dat.s->s);
	 return 0;
      }
      if(!dictContainsKey(dic, "peers"))
      {
	 loggerLog(LOG_ERR, "Malformed tracker data: No failure reason or peers returned.\n");
	 return 0;
      }

      if(dictContainsKey(dic, "interval"))
         t->announceInterval = ((be_dat *)dictGet(dic, "interval"))->dat.i;
      if(dictContainsKey(dic, "min interval"))
         t->minAnnounceInterval = ((be_dat *)dictGet(dic, "min interval"))->dat.i;
      if(dictContainsKey(dic, "complete"))
         t->totalSeeders = ((be_dat *)dictGet(dic, "complete"))->dat.i;
      if(dictContainsKey(dic, "complete"))
         t->totalLeechers = ((be_dat *)dictGet(dic, "incomplete"))->dat.i;


      be_dat *peersVal = dictGet(dic, "peers");
      if(peersVal->type == BE_LIST)
      {
//	 loggerLog(LOG_DEBUG,"Tracker returned a doubly non-compact announce. Unsupported.\n");
         list_t *peers = peersVal->dat.l;
	 resetList(peers);
	 while(!endList(peers))
	 {
	    be_dat *tmpdat = getObject(peers);
            if(!tmpdat->type == BE_DICT)
            {
               loggerLog(LOG_WARN, "Tracker announce returned list of peers that weren't dicts. Malformed.\n");
               continue;
            }
	    dict_t *peerd = tmpdat->dat.d;
            if(!dictContainsKey(peerd, "ip") || !dictContainsKey(peerd,"port"))
            {
               loggerLog(LOG_WARN, "Malformed peer in announce. Dict from list did nto contain ip or port.\n");
               continue;
            }
            peer_t *apeer = peerInit(ceil((double)t->totalSize / (double)t->pieceLen), t->pieceLen);
            apeer->port = ((be_dat *)dictGet(peerd,"port"))->dat.i;

            char *ipstr = (((be_dat *)dictGet(peerd,"ip"))->dat.s->s);
            struct hostent *tmpConvert = gethostbyname(ipstr);
            if(tmpConvert == NULL)
            {
               loggerLog(LOG_WARN, "Error resolving peer address.\n");
               continue;
            }
            //apeer->ip = ((struct in_addr)(tmpConvert->h_addr_list[0])).s_addr;
//#warning Torrent Announcing a list of dicts doesnt work
            listLock(t->inactpeers);
            addObject(t->inactpeers, apeer);
            listRelease(t->inactpeers);
	 }
      }
      else if(peersVal->type == BE_STR)
      {
	 mystr *peerstr = peersVal->dat.s;
	 int i;
	 for(i=0;i<peerstr->len;i+=6)
	 {
	    peer_t *apeer = peerInit(ceil((double)t->totalSize / (double)t->pieceLen), t->pieceLen);
	    apeer->ip = ntohl(*(unsigned int *)(peerstr->s+i));
	    apeer->port = ntohs(*(unsigned short*)(peerstr->s+i+4));
            listLock(t->inactpeers);
	    addObject(t->inactpeers, apeer);
            listRelease(t->inactpeers);
	 }
      }
      else
      {
	 loggerLog(LOG_ERR, "Malformed tracker data: 'peers' was not a list or string.\n");
	 return 0;
      }

//      beFree(resp); //bluh, its pointers are in use. No deep free for us (yet)
   }
   else
      printf("UDP tracker support not done. Sorry bro.\n");


   return 1;
}

void torrentShufflePeers(torrent_t *t, char *peer_id)
{
   if(((gettime() - t->lastPeerShuffle) > t->shuffleInterval))
   {
      if((listGetSize(t->actpeers) < configGetInt(conf, "MaxPeers")) && (listGetSize(t->inactpeers) > 0))
      {
         torrentActivatePeer(t, peer_id);
         t->lastPeerShuffle = gettime(); //Don't do a true shuffle until we have maxpeers
      }
      else
      //torrentDeactivatePeer(t); //Not doing this yet
      //torrentActivatePeer(t, peer_id);
         t->lastPeerShuffle = gettime();
   }
}

void torrentActivatePeer(torrent_t *t, char *peer_id)
{
   loggerLog(LOG_INFO, "Trying to add peer...\n");
   int i,randInact;
   listLock(t->inactpeers);
   if(listGetSize(t->inactpeers) == 0)
   {
      loggerLog(LOG_INFO, "Couldn't because empty inactpeers.\n");
      listRelease(t->inactpeers);
      return;
   }
   resetList(t->inactpeers);
   randInact = rand() % (listGetSize(t->inactpeers));
   for(i=0;i<randInact;i++) getObject(t->inactpeers);

   peer_t *toAdd = getObject(t->inactpeers);

   if(toAdd == NULL) //Shouldn't happen :(
   {
      loggerLog(LOG_INFO, "Error adding peer. Oh well.\n");
      listRelease(t->inactpeers);
      return;
   }
   removeObject(t->inactpeers, toAdd);
   listRelease(t->inactpeers);

   loggerLog(LOG_INFO, "Adding a new peer to the torrent %s ip=%x.\n", torrentGetName(t), toAdd->ip );

   if(!peerHandshake(toAdd, peer_id, t->infohash))
   {
      listLock(t->actpeers);
      addObject(t->actpeers,toAdd);
      listRelease(t->actpeers);
   }

   if(peerSendInterested(toAdd) == PEER_SHUTDOWN)
      torrentDeactivatePeer(t, toAdd);
}

void torrentAnnounceIfNeeded(torrent_t *t, char *peer_id, int port)
{ 
   if(((gettime() - t->lastAnnounce) > t->announceInterval))
   {
      if(!torrentTrackerAnnounce(t, peer_id, port))
         loggerLog(LOG_ERR, "Error reaching tracker for %s.\n", torrentGetName(t));
      else
         loggerLog(LOG_INFO, "Announced for torrent %s.\n", torrentGetName(t));
   }
}

void torrentForceAnnounce(torrent_t *t, char *peer_id, int port)
{
   if(!torrentTrackerAnnounce(t, peer_id, port))
   {
      loggerLog(LOG_ERR, "Error reaching tracker for %s during force announce.\n", torrentGetName(t));
   }
   else
      loggerLog(LOG_INFO, "Force announced for torrent %s.\n", torrentGetName(t));
}

char *torrentGetName(torrent_t *t)
{
   if(t->singleFile)
      return t->file.s->name;
   else
      return t->file.m->name;
}

void torrentPrint(torrent_t *t)
{
   printf("Torrent:\n");
   printf("\ttype: %s and %s\n", (t->singleFile) ? "singlefile" : "multifile", t->priv ? "private" : "public");
   printf("\tannounce: %s\n\tPiece Length: %lu\n", t->announce, t->pieceLen);
   printf("\ttotal size:%llu\n", t->totalSize);
   printf("\tInfo hash : ");
   int i;
   for(i=0;i<20;i++)
      printf("%02X", ((uint8_t *)t->infohash)[i]);
   printf("\n");
   printf("\tfile info: ");
   if(t->singleFile)
   {
      printf("\n\t\tname: %s\n\t\tlength: %lu\n", t->file.s->name, t->file.s->len);
   }
   else
   {
      printf("\n\t%s\n", t->file.m->name);
      list_t *sfiles = t->file.m->files;
      resetList(sfiles);
      while(!endList(sfiles))
      {
	 sfile *s = getObject(sfiles);
	 printf("\t%s\tlength: %lu\n", s->name, s->len);
      }
   

   }
}

void torrentKeepPeersAlive(torrent_t *t)
{
   peer_t *apeer;
   listLock(t->actpeers);
   for(resetList(t->actpeers);!endList(t->actpeers);)
   {
      apeer = getObject(t->actpeers);
      if((gettime() - apeer->my_alive) > 60)
      {
         loggerLog(LOG_INFO, "Sending keep alive\n");
         int ret = peerSendKeepAlive(apeer);
         if(ret == 0)
         {
            loggerLog(LOG_INFO, "Keep alive sent successfully.\n");
            apeer->my_alive = gettime();
         }
         else if(ret == PEER_SHUTDOWN)
            torrentDeactivatePeer(t, apeer);
      }
   }
   
   listRelease(t->actpeers);
}

void torrentCheckForData(torrent_t *t)
{
   peer_t *apeer;
   listLock(t->actpeers);
   list_t *tmpCopyActPeers = createList();
   for(resetList(t->actpeers);!endList(t->actpeers);)
      addObject(tmpCopyActPeers, getObject(t->actpeers));
   listRelease(t->actpeers);

   for(resetList(tmpCopyActPeers);!endList(tmpCopyActPeers);)
   {
      apeer = getObject(tmpCopyActPeers);
      int ret = peerCheckRecieved(apeer, ((void *)t), (void (*)(void *, peer_t *, netmsg_t))torrentPeerEventCallback);
      if(ret == PEER_SHUTDOWN)
         torrentDeactivatePeer(t, apeer);
   }
   clearList(tmpCopyActPeers);
   free(tmpCopyActPeers);
}

void torrentDeactivatePeer(torrent_t *t, peer_t *p)
{
   int u1,u2;
   u1 = u2 = 0;
   if(!listIsLocked(t->actpeers))
   {
      u1 = 1; listLock(t->actpeers);
   }
   if(!listIsLocked(t->inactpeers))
   {
      u2 = 1; listLock(t->inactpeers);
   }
   peerDisconnect(p);
   peer_t *resp = removeObject(t->actpeers, p);
   if(resp == NULL)
   {
      loggerLog(LOG_WARN, "Failed to remove active peer that should have existed.\n");
   }
   else
   {
      addObject(t->inactpeers, p);
   }
   
   if(u1)
      listRelease(t->actpeers);
   if(u2)
      listRelease(t->inactpeers);

}

void torrentPeerEventCallback(torrent_t *t, peer_t *p, netmsg_t msg)
{
   loggerLog(LOG_INFO, "Got message with id %d, length %d.\n",
      msg.message_ID, msg.len_prefix);
   if(msg.message_ID == KEEPALIVE && msg.len_prefix == 0)
      p->peer_alive = gettime();
   else if(msg.message_ID == CHOKE)
      p->peer_choking = 1;
   else if(msg.message_ID == UNCHOKE)
      p->peer_choking = 0;
   else if(msg.message_ID == INTERESTED)
      p->peer_interest= 1;
   else if(msg.message_ID == NOTINTERESTED)
      p->peer_interest= 0;
   else if(msg.message_ID == HAVE)
      peerSetHave(p, ntohl(*((uint32_t *)(msg.payload))));
   else if(msg.message_ID == BITFIELD)
      peerSetBitfield(p, msg.len_prefix - 1, msg.payload);
   else if(msg.message_ID == REQUEST && msg.len_prefix == 13)
   {
      unsigned int ndx = ntohl(*((uint32_t *)msg.payload));
      unsigned int offset = ntohl(*((uint32_t *)(msg.payload + 4)));
      unsigned int len = ntohl(*((uint32_t *)(msg.payload + 8)));
      peerSetRequest(p, ndx, offset, len);
   }
   else if(msg.message_ID == PIECE && msg.len_prefix >= 9)
   {
      unsigned int ndx = ntohl(*((uint32_t *)msg.payload));
      unsigned int offset = ntohl(*((uint32_t *)(msg.payload + 4)));
      unsigned char *data = (unsigned char *)(msg.payload + 8);
      torrentReceiveBlock(t, ndx, offset, data, msg.len_prefix - 9);
      p->my_reqs--;
   }
   else if(msg.message_ID == CANCEL && msg.len_prefix == 13)
   {
      unsigned int ndx = ntohl(*((uint32_t *)msg.payload));
      unsigned int offset = ntohl(*((uint32_t *)(msg.payload + 4)));
      unsigned int len = ntohl(*((uint32_t *)(msg.payload + 8)));
      peerCancelRequest(p, ndx, offset, len);
   }
   else if(msg.message_ID == PORT && msg.len_prefix == 3)
   {
      //DHT MUMBO JUMBO WOULD GO HERE.
      //WILL GO HERE
      //EVENTUALLY
      //dht is being delayed for the forseeable future due to my inability
      //to spell foreseeable. Also, not having this means no magnet links either.
      //Anyways, we'll have to do this eventually. After we get regular torrenting working
      //and probably after this gets graded.
   }
   else
      loggerLog(LOG_WARN, "Received unknown message from peer %x.\n", p->ip);
   free(msg.payload);
}


void torrentReceiveBlock(torrent_t *t, unsigned int ndx, unsigned int offset, unsigned char *data, unsigned int len)
{
   loggerLog(LOG_INFO, "Received block of length %d for torrent %s.\n", len, torrentGetName(t));

   tdiskop_t op;
   op.manager = diskman;
   list_t *tmpList;
   if(t->singleFile)
   {
      tmpList = createList();
      addObject(tmpList, t->file.s);
      op.filelist = tmpList;
      op.baseDir = ".";
   }
   else
   {
      op.filelist = t->file.m->files;
      op.baseDir = t->file.m->name;
   }
   op.index = ndx;
   op.offset = offset;
   op.datalen = len;
   op.piecelen = t->pieceLen;

   tdisk_writeData(op, data);

   // Cleanup used memory
   if(t->singleFile)
   {
      clearList(tmpList);
      free(tmpList);
   }

   unsigned int i;
   unsigned int pieces = ceil((double)t->totalSize / (double)t->pieceLen);

   dictLock(t->activeReqsRcved);
   if(dictSize(t->activeReqsRcved) > 0) //If we have in-progress pieces
   {
      if(dictContainsKey(t->activeReqsRcved, &ndx))
      {
      bitfield_t *bits = dictGet(t->activeReqsRcved, &ndx);
      bitfieldSet(bits, offset / BLOCK_SIZE, 1);
      }
      //Check if those pieces are complete and if so finish them.
      list_t *d = dictTraversal(t->activeReqsRcved);
      for(resetList(d);!endList(d);)
      {
         //Key=piece index, val = bitfield of blocks
         dict_key_val_t *kv = getObject(d);
         ndx = *((unsigned int *)(kv->key));
         if(ndx == (pieces - 1)) //Final piece is special. Might not be a full piece
         {
            int isFull = 1;
            for(i=0;i<(t->pieceLen / BLOCK_SIZE);i++)
            {
               //Ignore blocks beyond the true end of the torrent
               if(t->totalSize < (ndx * t->pieceLen + i * BLOCK_SIZE)) break;
               if(bitfieldGet(kv->val, i) == 0)
                  isFull = 0;
            }
            if(isFull)
               torrentHavePiece(t, ndx);
         }
         else if(bitfieldFull(kv->val))
         {
            torrentHavePiece(t, ndx);
         }
      }
      clearListAndFreeElements(d);
   }
   dictRelease(t->activeReqsRcved);

   torrentIncreaseDownloaded(t, len);
}

void torrentRequestBlocks(torrent_t *t)
{
   if(t->left == 0)
      return;  // Return if there's nothing left to download
   if(bitfieldFull(t->piecebitfield))
      return;  // Return if we have every piece
   unsigned int i, ndx, offset, len, someoneHas, pieces, piece;
   unsigned int *heapNdx = malloc(sizeof(unsigned int));
   pieces = ceil((double)t->totalSize / (double)t->pieceLen);

   if(bitfieldFull(t->piecebitfield))
   {
      //Our work here is done
      if(t->status == STAT_LEECHING)
         t->status = STAT_SEEDING;
      return;
   }

   piece = rand() % (pieces);
   while(bitfieldGet(t->piecebitfield, piece))
      piece = rand() % (pieces); //Pick a piece we don't have
   someoneHas = 0; //Does anyone have our random piece
   peer_t *apeer;
   if((dictSize(t->activeReqs) < configGetInt(conf, "MaxActivePieces")))
   {
      listLock(t->actpeers);
      for(i=0;i<pieces && !someoneHas;i++)
      {
         for(resetList(t->actpeers);!endList(t->actpeers);)
         {
            apeer = getObject(t->actpeers);
            if(peerGetHave(apeer, i))
            {
               someoneHas = 1;
               break;
            }
         }
         piece = (piece + 1) % pieces;
         while(bitfieldGet(t->piecebitfield, piece))
            piece = (piece + 1) % pieces;
      }
      listRelease(t->actpeers);
      if(!someoneHas) //Everyone at 0%? That sucks, give up.
         return;
      bitfield_t *newBitfield;
      bitfield_t *newBitfield2;
      newBitfield = bitfieldInit(t->pieceLen / BLOCK_SIZE);
      newBitfield2= bitfieldInit(t->pieceLen / BLOCK_SIZE);
      *heapNdx = piece;
      dictPut(t->activeReqs, heapNdx, newBitfield); //We want this piece
      dictLock(t->activeReqsRcved);
      dictPut(t->activeReqsRcved, heapNdx, newBitfield2); //What we have of it
      dictRelease(t->activeReqsRcved);
   }
   
   /*
    * Loop through all the pieces we're actively working on
    * for each of those pieces loop through all the peers and 
    * see who has it. Request blocks from them if they do have it.
    * only request if the peer's active req limit isn't reached yet
    */
   list_t *wanted = dictTraversal(t->activeReqs);
   for(resetList(wanted);!endList(wanted);)
   {
      dict_key_val_t *kv = getObject(wanted);
      ndx = *(unsigned int *)kv->key;
      bitfield_t *val = kv->val;

      for(i = 0; i < t->pieceLen / BLOCK_SIZE;i++)
      {
         if(bitfieldGet(val, i) == 1)
            continue; //Already have it requested
   
         len = BLOCK_SIZE;
         if((ndx * t->pieceLen + i * BLOCK_SIZE) > t->totalSize) break; 
         //If this is the final partial piece our length request is shorter
         if(((t->totalSize > (ndx * t->pieceLen + i * BLOCK_SIZE))) && ((t->totalSize - (ndx * t->pieceLen + i * BLOCK_SIZE)) < BLOCK_SIZE))
         {
            len = t->totalSize - (ndx * t->pieceLen + i * BLOCK_SIZE);
         }

         offset = i * BLOCK_SIZE;
         listLock(t->actpeers);
         for(resetList(t->actpeers);!endList(t->actpeers);)
         {
            apeer = getObject(t->actpeers);
            if(apeer->peer_choking == 1) continue;
            if(apeer->my_reqs >= configGetInt(conf, "MaxQueuedReqs"))
               continue;
            if(peerGetHave(apeer, ndx) && (!peerRequested(apeer, ndx, offset)))
            {
               apeer->my_reqs++;
               bitfieldSet(val, i, 1);
            
               if(peerSendRequest(apeer, ndx, offset, len) == PEER_SHUTDOWN)
                  torrentDeactivatePeer(t, apeer);
               break;
            }
         }
         listRelease(t->actpeers);
      }
   }
   clearListAndFreeElements(wanted);
}

void torrentSendPieces(torrent_t *t) // Check for requests, send out data
{
   // Check all peers for requests

   
}

void torrentIncreaseDownloaded(torrent_t *t, unsigned int len)
{
   t->downloaded += len;
   t->left -= len;
   //printf("PERCENT IS %f\n\n", (double)t->downloaded / (double)t->totalSize);
}

char *torrentStatusString(torrent_t *t)
{
   char *ret;
   switch(t->status)
   {
      case STAT_COMPLETING:
         ret = strdup("Completing");
         break;
      case STAT_COMPLETE:
         ret = strdup("Complete");
         break;
      case STAT_SEEDING:
         ret = strdup("Seeding");
         break;
      case STAT_STARTING:
         ret = strdup("Starting");
         break;
      case STAT_LEECHING:
         ret = strdup("Leeching");
         break;
      case STAT_PAUSED:
         ret = strdup("Paused");
         break;
      case STAT_STOPPING:
         ret = strdup("Stopping");
         break;
      case STAT_STOPPED:
         ret = strdup("Stopped");
         break;
   }
   return ret;
}

double torrentDownloaded(torrent_t *t)
{
   return bitfieldPercent(t->piecebitfield) * 100;
}

void torrentHavePiece(torrent_t *t, unsigned int ndx)
{
   bitfieldSet(t->piecebitfield, ndx, 1);
   //bitfieldPrint(t->piecebitfield);
   bitfield_t *c = dictGet(t->activeReqs, &ndx);
   free(c->bitfield);
   free(c);
   dictRemove(t->activeReqs, &ndx);
   dictRemove(t->activeReqsRcved, &ndx);
   listLock(t->actpeers);
   for(resetList(t->actpeers);!endList(t->actpeers);)
   {
      peer_t  *apeer = getObject(t->actpeers);
      //Stylin' have suppression
      //I'm not sure why, but I think have suppression caused problems
      if(configGetInt(conf, "UseHaveSuppression"))
      {
         if(!peerGetHave(apeer, ndx))
         {
            if(peerSendHave(apeer, ndx) == PEER_SHUTDOWN)
               torrentDeactivatePeer(t, apeer);
         }
      }
      else
         if(peerSendHave(apeer, ndx) == PEER_SHUTDOWN) //PEER_SHUTDOWN handling needs to be automatic. I got an idea. CALLBACKS. TWO OF EM
            torrentDeactivatePeer(t, apeer);
   }
   listRelease(t->actpeers);
   if(bitfieldFull(t->piecebitfield))
   {
      if(t->status == STAT_LEECHING)
         t->status = STAT_SEEDING;
   }
}
